package com.wpf.system.controller;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.wpf.system.common.response.ResultUtils;
import com.wpf.system.common.response.ResultVO;
import com.wpf.system.common.utils.JWTUtils;
import com.wpf.system.common.utils.MakeTree;
import com.wpf.system.common.utils.RedisUtils;
import com.wpf.system.common.utils.UploadUtils;
import com.wpf.system.common.utils.excelListener.UserExcelListener;
import com.wpf.system.entity.*;
import com.wpf.system.entity.dto.UserRoleDTO;
import com.wpf.system.entity.vo.*;
import com.wpf.system.security.LoginUser;
import com.wpf.system.service.UserRoleService;
import com.wpf.system.service.UserService;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/user")
public class UserController {

    /**
     * 上传头像时，允许使用的文件的最大大小，使用字节为单位
     */
    @Value("${system.avatar.max-size}")
    private int avatarMaxSize;

    @Value("${system.saveFileUrl}")
    private String saveFileUrl;

    @Value("${server.port}")
    private String port;

    /**
     * 上传头像时，允许使用的头像文件的MIME类型
     */
    @Value("${system.avatar.types}")
    private List<String> avatarTypes;

    @Resource
    private UserService userService;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private JWTUtils jwtUtils;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private UserRoleService userRoleService;

    @GetMapping("/todayLogin")
    public Long statisticsTodayLogin(@RequestParam("date") String time,@RequestParam("offset") int offset){
        Date date = DateUtil.offsetDay(DateUtil.parse(time),offset);
        return userService.count(new LambdaQueryWrapper<User>().between(User::getLoginTime, DateUtil.beginOfDay(date),DateUtil.endOfDay(date)));
    }

    @GetMapping("/{id}")
    public UserInfoVO getUserAllInfo(@PathVariable("id") Long id){ return userService.getUserAllInfo(id); }

    @GetMapping("/fieldUserManager")
    public List<User> getFieldUserManager(){ return userService.getFieldUserManager(); }

    @GetMapping("/list")
    public IPage<User> list(
                           @RequestParam("page") int page,
                           @RequestParam("pageSize") int pageSize,
                           @RequestParam("id") Long id,
                           @RequestParam(value = "deptId",required=false) Long deptId,
                           @RequestParam(value = "type",required=false) String type,
                           @RequestParam(value = "searchContent",required=false) String searchContent ){
        return userService.getUserExceptLoginUser(page,pageSize,id,deptId,type,searchContent);
    }

    @PutMapping("/status")
    public ResultVO editUserStatus(@RequestBody Long id){
        if (Convert.toBool(userService.editUserStatusById(id))){
            return ResultUtils.success("修改用户状态成功！");
        }else{
            return ResultUtils.error("修改用户状态失败！");
        }
    }

    @PreAuthorize("hasAuthority('sys:user:add')")
    @PostMapping
    public ResultVO addUser(@RequestBody User user ){
        User resultUser = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getNid,user.getNid()));
        System.out.println(resultUser);
        if(ObjectUtil.isNotNull(resultUser)){
            return ResultUtils.error("账号已存在！");
        }else {
            if(userService.addUser(user)){
                return ResultUtils.success("添加用户成功！");
            }else{
                return ResultUtils.error("添加用户失败！");
            }
        }
    }

    @PreAuthorize("hasAuthority('sys:user:import')")
    @PostMapping("/batch/import")
    public ResultVO addUserBatch(@RequestParam("file") MultipartFile file) {
        UserExcelListener listener = new UserExcelListener(userService);
        try {
            EasyExcel.read(file.getInputStream(), User.class, listener ).sheet(0).headRowNumber(1).doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
        int passNum = listener.getPassNum();
        int failNum = listener.getFailNum();
        if(passNum==0){
            return ResultUtils.error("导入用户信息失败！请检查EXCEL表格的数据是否有误！");
        }else{
            if(failNum==0){
                return ResultUtils.success("导入用户信息成功！共导入"+passNum+"条");
            }else{
                return ResultUtils.success("成功导入"+passNum+"条用户信息！ "+failNum+"条用户信息导入失败！");
            }
        }
    }

    @PutMapping
    public ResultVO editUser(@RequestBody User user ){
        User resultUser = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getNid,user.getNid()));
        if(resultUser != null && !resultUser.getId().equals(user.getId())){
            return ResultUtils.error("学号已存在！");
        }
        if(ObjectUtil.isNotNull(user.getPassword())){
            //密码加密
            user.setPassword(passwordEncoder.encode(user.getPassword()));
            if(userService.updateById(user)){
                return ResultUtils.success("重置密码成功！");
            }
            return ResultUtils.error("重置密码失败！");
        }else{
            if(userService.updateById(user)){
                return ResultUtils.success("修改用户信息成功！");
            }
            return ResultUtils.error("修改用户信息失败！");
        }
    }

    @GetMapping("/forgetPWD")
    public User checkUserCanForgetPW(@RequestParam("nid") String nid ){
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getNid,nid).isNotNull(User::getEmail).isNotNull(User::getCheckEmail);
        if(userService.getOne(queryWrapper)!=null){
            return userService.getOne(queryWrapper);
        }
        return null;
    }

    @PutMapping("/restPW")
    public ResultVO restUserPW(@RequestBody User user ){
        User userFromSQL = userService.getById(user);
        userFromSQL.setCheckPassword(user.getCheckPassword());
        userFromSQL.setPassword(passwordEncoder.encode(user.getPassword()));
        if(userService.updateById(userFromSQL)){
            return ResultUtils.success("重置密码成功！");
        }
        return ResultUtils.error("重置密码失败！");
    }

    @PutMapping("/editPW")
    public ResultVO editUserPW(@RequestBody EditPasswordVO editPasswordVO){
        if (StrUtil.isNotBlank(editPasswordVO.getOldPassword())){
            User resultUser = userService.getById(editPasswordVO.getId());
            String passwordFromSQL = resultUser.getPassword();
            if(passwordEncoder.matches(editPasswordVO.getOldPassword(),passwordFromSQL)){
                if(userService.editUserPassword(
                        editPasswordVO.getId(),
                        passwordEncoder.encode(editPasswordVO.getNewPassword()),
                        editPasswordVO.getCheckPassword())
                ){
                    return ResultUtils.success("修改密码成功！密码将在下次登录生效！");
                }else{
                    return ResultUtils.error("修改密码失败！");
                }
            }else {
                return ResultUtils.error("旧密码不正确！");
            }
        }else if(StrUtil.isBlank(editPasswordVO.getOldPassword())){
            if(userService.editUserPassword(
                    editPasswordVO.getId(),
                    passwordEncoder.encode(editPasswordVO.getNewPassword()),
                    editPasswordVO.getCheckPassword())
            ){
                return ResultUtils.success("重置密码成功！密码将在下次登录生效！");
            }else{
                return ResultUtils.error("重置密码失败！");
            }
        }
        return ResultUtils.error("请检查表单数据是否有误！");
    }

    @PreAuthorize("hasAuthority('sys:user:delete')")
    @DeleteMapping("/{id}")
    public ResultVO deleteUser(@PathVariable("id") Long id){
        if (userService.deleteUserById(id)){
            return ResultUtils.success("删除用户成功！");
        }else{
            return ResultUtils.error("删除用户失败！");
        }
    }

    @PreAuthorize("hasAuthority('sys:user:deletebatch')")
    @DeleteMapping("/batch/delete/{ids}")
    public ResultVO deleteUserBatch(@PathVariable("ids") String ids){
        int count = 0;
        String[] deleteIds = ids.split(",");
        for ( String id : deleteIds ){
            if(userService.deleteUserById(Convert.toLong(id))){ count++; }
        }
        if (count==deleteIds.length){
            return ResultUtils.success("成功删除"+count+"名用户！");
        }else{
            if(count==0){
                return ResultUtils.error("批量删除用户失败！");
            }else{
                return ResultUtils.success("成功删除"+count+"名用户！ "+(deleteIds.length-count)+"名用户删除失败！");
            }
        }
    }

    @PreAuthorize("hasAuthority('sys:user:userrole')")
    @PostMapping("/role")
    public ResultVO editUserRole(@RequestBody UserRoleDTO userRoleDTO){
        boolean flag = Convert.toBool(userRoleService.saveUserRole(userRoleDTO.getUid(), userRoleDTO.getRidList()));
        if (flag){
            return ResultUtils.success("分配角色成功！");
        }else{
            return ResultUtils.error("分配角色失败！");
        }
    }

    @PostMapping("/avatar")
    public ResultVO avatar(@RequestParam("avatarFile") MultipartFile file, @RequestParam("id") Long id, @RequestParam("nowAvatar") String nowAvatar ) {
        if (file.isEmpty()){
            return ResultUtils.error("上传头像失败！请选择有效的图片！");
        }
        if (file.getSize() > avatarMaxSize){
            return ResultUtils.error("上传头像失败！图片大小超过"+(avatarMaxSize/1024/1024)+"M！");
        }
        if (!avatarTypes.contains(file.getContentType())){
            return ResultUtils.error("上传头像失败！图片应为以下格式：\n\n"+avatarTypes);
        }
        String serverAddress = saveFileUrl +":"+ port;
        String reAvatar = "";
        if(StringUtils.isNotBlank(nowAvatar)){
            reAvatar = nowAvatar.replace(serverAddress,"static");
        }

        String saveUrl = serverAddress + UploadUtils.uploadAvatar(file,Convert.toStr(id),reAvatar);
        if(Convert.toBool(userService.editUserAvatarById(id,saveUrl))){
            return ResultUtils.success("上传头像成功！",saveUrl);
        }
        return ResultUtils.error("上传头像失败！");
    }

    @PutMapping("/emailStatus")
    public ResultVO editEmailStatus(@RequestBody Long id){
        boolean flag = userService.editUserEmailStatus(id);
        if (flag){
            return ResultUtils.success("解绑邮箱成功！");
        }else{
            return ResultUtils.error("解绑邮箱失败！");
        }
    }


//=====================================用户登录相关==============================================

    @GetMapping("/getInfo")
    public LoginInfoVO getInfo(){
        //获取登录用户相关的信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //获取登录用户信息
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        LoginInfoVO loginInfoVO = new LoginInfoVO();
        loginInfoVO.setId(loginUser.getUser().getId());
        loginInfoVO.setName(loginUser.getUser().getUsername());
        loginInfoVO.setAvatar(loginUser.getUser().getAvatar());
        loginInfoVO.setIntroduction(loginUser.getUser().getRemark());
        //获取权限字段
        List<Menu> permissionList = loginUser.getUser().getPermissionList();
        Object[] toArray = permissionList.stream().filter(Objects::nonNull).map(Menu::getPerms).toArray();
        loginInfoVO.setPermission_routes(toArray);
        return loginInfoVO;
    }

    @GetMapping("/getMenuList")
    public List<RouterVO> getMenuList(){
        //获取用户相关的信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //获取登录用户信息
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        //获取权限
        List<Menu> permissionList = loginUser.getUser().getPermissionList();
        //只需要获取目录和菜单
        List<Menu> collect = permissionList.stream().filter(item -> item != null && item.getType()!=2).collect(Collectors.toList());
        return MakeTree.makeRouter(collect, 0L);
    }

    @PostMapping("/refreshToken")
    public ResultVO refreshToken(HttpServletRequest request){
        //获取token
        String token = request.getParameter("token");
        if(StringUtils.isEmpty(token)){
            token = request.getHeader("token");
        }
        //获取用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserDetails userDetail = (UserDetails) authentication.getPrincipal();

        //验证token
        Boolean flag = jwtUtils.validateToken(token, userDetail);
        if(!flag){
            return ResultUtils.error("token验证失败");
        }
        //获取刷新token
        String refreshToken = jwtUtils.refreshToken(token);
        //获取新的token过期时间
        Long expireTime =  Jwts.parser()
                .setSigningKey(jwtUtils.getSecret())
                .parseClaimsJws(refreshToken)
                .getBody().getExpiration().getTime();
        TokenVO tokenEntity  = new TokenVO();
        tokenEntity.setExpireTime(expireTime);
        tokenEntity.setToken(refreshToken);
        //把原来的token删除
        String delKey = "token_"+token;
        redisUtils.del(delKey);
        //把刷新的token存到redis
        String newKey = "token_"+refreshToken;
        redisUtils.set(newKey,refreshToken,jwtUtils.getExpiration() / 1000);
        return ResultUtils.success("成功",tokenEntity);
    }
}


